/*
        Copyright (C) 2007  Aaron Defazio

        This program is free software; you can redistribute it and/or
        modify it under the terms of the GNU General Public License
        as published by the Free Software Foundation; either version 2
        of the License, or (at your option) any later version.

        This program is distributed in the hope that it will be useful,
        but WITHOUT ANY WARRANTY; without even the implied warranty of
        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        GNU General Public License for more details.

        You should have received a copy of the GNU General Public License
        along with this program; if not, write to the Free Software
        Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.ComponentModel;
using System.Windows.Forms;
using PluginInterface;
using System.IO;
using System.Reflection;

namespace AppLn
{
    public class Controller
    {
        //Probably not needed, but meh
        private string _Input;
        private string ReSearchInput;   //Used for bgw, if search was done while already searching
        public delegate void DSearch();
        public event DSearch SearchFinished;

        private string PluginDirectory = "plugins";

        public string Input // READ ONLY
        {
            get { return _Input; }
           // set { _Input = value; }
        }

        private List<RObject> _ResultOptions; //Databound by the view

        public List<RObject> ResultOptions
        {
            get { return _ResultOptions; }
            set { _ResultOptions = value; }
        }

        List<PluginObject> Plugins;
        PluginObject CurrentSearchPlugin;

        BackgroundWorker bgw;
        Idle IdleCounter;

        public Controller()
        {
            PluginDirectory = Application.StartupPath + Path.DirectorySeparatorChar + PluginDirectory;
            _ResultOptions = new List<RObject>();
            _Input = "";
            ReSearchInput = "";
            Plugins = new List<PluginObject>();

            bgw = new BackgroundWorker();
            bgw.DoWork += new DoWorkEventHandler(bgw_DoWork);
            bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgw_RunWorkerCompleted);

            AddPlugins();

            IdleCounter = new Idle();
            IdleCounter.UserIsIdle += new EventHandler(IdleCounter_UserIsIdle);

        }

        void IdleCounter_UserIsIdle(object sender, EventArgs e)
        {
            foreach (PluginObject p in Plugins)
            {
                p.Plugin.ReIndex();
            }
        }

        void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Result == null)
            {
                System.Windows.Forms.MessageBox.Show("Error Doing Asyn search");
                return;
            }
            ResultOptions = (List<RObject>)e.Result;
            SearchFinished();   //Let any watchers know we finished the search

            if (ReSearchInput != "")
            {
                string temp = ReSearchInput;    //So we dont get in a infinite loop, stupid multithreading
                ReSearchInput = "";
                InputChanged(temp);
            }
            
        }

        void bgw_DoWork(object sender, DoWorkEventArgs e)
        {
            //try
            //{
                SearchObject so = (SearchObject)e.Argument;
                IPlugin Plugin = (IPlugin)so.Plugin;
                e.Result = Plugin.DoQuery(so.SearchTerm);
            //}
            //catch( Exception ex)
            //{
            //    //Just do nothing if passed argument is wrong type, or query crashes
            //    e.Result = null;
            //}           
        }

        private void AddPlugins()
        {
            string[] files = Directory.GetFiles(PluginDirectory, "*.dll", SearchOption.TopDirectoryOnly);
            foreach (string file in files)
            {
                Assembly asm = Assembly.LoadFile(Path.GetFullPath(file));                
                foreach (Type t in asm.GetTypes())
                {
                    if (t.GetInterface("PluginInterface.IPlugin") != null)
                    {
                        if (!t.IsAbstract && !t.IsInterface)
                        {
                            IPlugin plugin = (IPlugin)Activator.CreateInstance(t);
                            Plugins.Add(new PluginObject(plugin.Shortcut, plugin));
                            plugin.Initilise();
                        }
                    }
                }
            }

        }

        public void close()
        {
            foreach (PluginObject p in Plugins)
            {
                p.Plugin.close();
            }
        }

        public void Open(string key)
        {
            string sc;
            string st;
            if (ReSearchInput == "")
            {
                ParseQuery(_Input, out sc, out st);
                CurrentSearchPlugin.Plugin.Open(key, st);
            }
            else
            {
                ParseQuery(ReSearchInput, out sc, out st);
                CurrentSearchPlugin.Plugin.Open(key, st);
            }
        }

        /// <summary>
        /// Called by the view to do whatever querying etc. needed to change the ResultOptions list.
        /// Search Shortcut is determined to be everything before the first ':' character. If no match
        /// Exists, we just do the default search, tied to ""(empty string). Likewise, if no ':' we 
        /// do the default search. 
        /// </summary>
        /// <param name="Input">Query string</param>
        public void InputChanged(string Input)
        {
            if (bgw.IsBusy)
            {
                ReSearchInput = Input;
                return;
            }

            if (_Input == Input)
                return; //Dont search if its not needed
            _Input = Input;            

            //work out the shortcut string
            string Shortcut;
            string SearchTerm;

            ParseQuery(Input, out Shortcut, out SearchTerm);

            //TODO: Expand this to work with multiple matches, instead of just the first

            //Find plugin with matching shortcut from the Plugin list
            PluginObject FirstMatch = Plugins.Find(delegate(PluginObject p) { return p.Shortcut == Shortcut; });
            if (FirstMatch == null)
            {
               FirstMatch = Plugins.Find(delegate(PluginObject p) { return p.Shortcut == ""; });
               Debug.Assert(FirstMatch != null);    //there beter be some match!
            }

            CurrentSearchPlugin = FirstMatch;
            //Do search async
            bgw.RunWorkerAsync(new SearchObject(SearchTerm, FirstMatch.Plugin));

        }

        private static void ParseQuery(string Input, out string Shortcut, out string SearchTerm)
        {
            char[] delim = { ':' };
            string[] splitInput = Input.Split(delim, 2, StringSplitOptions.None);


            if (splitInput.Length == 1)
            {
                //default search
                Shortcut = "";
                SearchTerm = splitInput[0];
            }
            else
            {
                Shortcut = splitInput[0];
                SearchTerm = splitInput[1];
            }
        }



    }

    public struct SearchObject
    {
        public string SearchTerm;
        public IPlugin Plugin;
        public SearchObject(string SearchTerm, IPlugin Plugin)
        {
            this.SearchTerm = SearchTerm;
            this.Plugin = Plugin;
        }
    }

    class PluginObject
    {
        //The part before the ':' in a search like "w:fish" or "g:warez" or an empty string for default
        public string Shortcut;

        public IPlugin Plugin;

        public PluginObject(string Shortcut, IPlugin Plugin)
        {
            this.Shortcut = Shortcut;
            this.Plugin = Plugin;
        }

    }
}
